﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using AstFramework.Engine.Binding;
using AstFramework.Model;
using VulcanEngine.IR.Ast;
using VulcanEngine.IR.Ast.Table;
using VulcanEngine.IR.Ast.Task;
using VulcanEngine.IR.Ast.Transformation;

namespace AstLowerer.Capabilities
{
    public static class LateArrivingLowerer
    {
        public static void ProcessLateArrivingTable(AstTableNode astTableNode)
        {
            var lateArrivedColumn = new AstTableColumnNode(astTableNode) { Name = "_LateArrived", IsNullable = true, ColumnType = ColumnType.DateTime2, IsAutoGenerated = true };
            var markLateColumn = new AstTableColumnNode(astTableNode) { Name = "_IsLate", IsNullable = true, ColumnType = ColumnType.Boolean, IsAutoGenerated = true, Computed = "_LateArrived IS NOT NULL PERSISTED NOT NULL" };
            astTableNode.Columns.Add(lateArrivedColumn);
            astTableNode.Columns.Add(markLateColumn);
        }

        public static void ProcessLateArrivingLookupTransformations(SymbolTable symbolTable) ////HashSet<AstEtlRootNode> astEtlRootNodes)
        {
            var snapshotSymbolTable = new List<IReferenceableItem>(symbolTable);
            foreach (var astNamedNode in snapshotSymbolTable)
            {
                var lookup = astNamedNode as AstLateArrivingLookupNode;
                if (lookup != null && astNamedNode.FirstThisOrParent<ITemplate>() == null)
                {
                    ProcessLateArrivingLookup(lookup);
                }
            }
        }

        private static void ProcessLateArrivingLookup(AstLateArrivingLookupNode lookup)
        {
            AstLowererValidation.ValidateLateArrivingLookup(lookup);

            var workflowFragment = new List<AstTransformationNode>();

            AstLookupNode codegenLookup = CreateLookupNode(lookup);
            AstOleDBCommandNode insertPlaceholder = CreateInsertNode(lookup, codegenLookup);
            AstUnionAllNode unionAll = CreateUnionAllNode(lookup, codegenLookup, insertPlaceholder);

            workflowFragment.Add(codegenLookup);
            workflowFragment.Add(insertPlaceholder);
            workflowFragment.Add(unionAll);

            Utility.Replace(lookup, workflowFragment);
        }

        private static AstUnionAllNode CreateUnionAllNode(AstLateArrivingLookupNode lookup, AstLookupNode codegenLookup, AstOleDBCommandNode insertPlaceholder)
        {
            var unionAll = new AstUnionAllNode(lookup.ParentItem) { Name = String.Format(CultureInfo.InvariantCulture, "__LALookupUnionAll_{0}", lookup.Name) };
            unionAll.InputPaths.Add(new AstDataflowMappedInputPathNode(unionAll) { OutputPath = codegenLookup.MatchPath });
            unionAll.InputPaths.Add(new AstDataflowMappedInputPathNode(unionAll) { OutputPath = insertPlaceholder.OutputPath });

            foreach (var outgoingPath in lookup.OutputPath.References)
            {
                if (outgoingPath.ReferencingItem != null && outgoingPath.PropertyName != null)
                {
                    PropertyInfo propertyInfo = outgoingPath.GetType().GetProperty(outgoingPath.PropertyName);
                    if (propertyInfo != null)
                    {
                        propertyInfo.SetValue(outgoingPath.ReferencingItem, unionAll.OutputPath, null);
                    }
                }
            }

            return unionAll;
        }

        private static AstOleDBCommandNode CreateInsertNode(AstLateArrivingLookupNode lookup, AstLookupNode codegenLookup)
        {
            var insertPlaceholder = new AstOleDBCommandNode(lookup.ParentItem)
                                        {
                                            Name = String.Format(CultureInfo.InvariantCulture, "__LALookupPlaceholderInsert_{0}", lookup.Name),
                                            Connection = lookup.Table.Connection,
                                        };

            insertPlaceholder.Query = new AstTransformationMappedQueryNode(insertPlaceholder) { Body = TableLowerer.EmitInsertDefaultRowStatement(lookup.Table) };
            insertPlaceholder.InputPath = new AstDataflowMappedInputPathNode(insertPlaceholder) { OutputPath = codegenLookup.NoMatchPath };
            return insertPlaceholder;
        }

        private static AstLookupNode CreateLookupNode(AstLateArrivingLookupNode lookup)
        {
            var codegenLookup = new AstLookupNode(lookup.ParentItem)
                                    {
                                        Name = String.Format(CultureInfo.InvariantCulture, "__LALookupEntryPoint_{0}", lookup.Name),
                                        Connection = lookup.Table.Connection
                                    };
            var requiredColumns = new List<string>();
            foreach (var input in lookup.Inputs)
            {
                if (!requiredColumns.Contains(input.RemoteColumnName))
                {
                    requiredColumns.Add(input.RemoteColumnName);
                }

                codegenLookup.Inputs.Add(input);
            }

            foreach (var output in lookup.Outputs)
            {
                if (!requiredColumns.Contains(output.RemoteColumnName))
                {
                    requiredColumns.Add(output.RemoteColumnName);
                }

                codegenLookup.Outputs.Add(output);
            }

            codegenLookup.Query = new AstQueryNode(codegenLookup) { Body = TableLowerer.EmitSelectAllStatement(lookup.Table, requiredColumns) };

            if (lookup.InputPath != null)
            {
                codegenLookup.InputPath = new AstDataflowMappedInputPathNode(codegenLookup) { OutputPath = lookup.InputPath.OutputPath };
            }

            return codegenLookup;
        }
    }
}